package com.sikaryofficial.backend.manager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sikaryofficial.backend.annotation.TenantFilter;
import com.sikaryofficial.backend.constant.BlockTypeEnum;
import com.sikaryofficial.backend.constant.MyStatisticsTypeEnum;
import com.sikaryofficial.backend.constant.StoreSourceEnum;
import com.sikaryofficial.backend.domain.dto.req.StoreDetailReq;
import com.sikaryofficial.backend.domain.dto.req.StoreEvaluateListReq;
import com.sikaryofficial.backend.domain.dto.req.StoreEvaluateSaveReq;
import com.sikaryofficial.backend.domain.dto.resp.StoreListDTO;
import com.sikaryofficial.backend.domain.entity.Address;
import com.sikaryofficial.backend.domain.entity.Store;
import com.sikaryofficial.backend.domain.entity.StoreEvaluate;
import com.sikaryofficial.backend.domain.entity.StoreEvaluateExt;
import com.sikaryofficial.backend.domain.mapping.StoreMapping;
import com.sikaryofficial.backend.service.IAttachmentService;
import com.sikaryofficial.backend.service.IPointsDetailStrategy;
import com.sikaryofficial.backend.service.IStoreEvaluateExtService;
import com.sikaryofficial.backend.service.IStoreEvaluateService;
import com.sikaryofficial.backend.service.IStoreService;
import com.sikaryofficial.backend.service.statistics.MyStatisticsEvent;
import com.sikaryofficial.backend.service.statistics.MyStatisticsProcessor;
import com.sikaryofficial.backend.service.store.StoreCacheEvent;
import com.sikaryofficial.backend.service.store.StoreCacheService;
import com.sikaryofficial.backend.service.useraction.cache.UserActionCacheService;
import com.sikaryofficial.common.core.constant.PointRuleConstant;
import com.sikaryofficial.common.core.exception.ServiceException;
import com.sikaryofficial.common.redis.service.RedisService;
import com.sikaryofficial.common.security.utils.SecurityUtils;
import com.sikaryofficial.system.api.model.att.AttachmentResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.sikaryofficial.backend.constant.RedisCacheKey.HISMK_STORE_EVALUATE_LIST_CACHE;
import static com.sikaryofficial.backend.utils.StoreUtil.buildStoreMd5;

/**
 * @author : qinjinyuan
 * @desc : 商店分享管理服务
 * @date : 2023/10/30 15:39
 */
@Service
@Slf4j
public class StoreEvaluateManager {

    @Autowired
    private IStoreEvaluateService storeSharedService;
    @Autowired
    private IStoreService storeService;
    @Autowired
    private IAttachmentService attachmentService;
    @Resource
    private Map<String, IPointsDetailStrategy> pointsDetailStrategyMap;
    @Autowired
    private StoreCacheService storeCacheService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private MyStatisticsProcessor myListener;
    @Autowired
    private UserActionCacheService userActionCacheService;
    @Resource
    private ApplicationEventPublisher publisher;
    @Autowired
    private IStoreEvaluateExtService evaluateExtService;

    /**
     * 保存店铺评价记录
     *
     * @param req
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean storeEvaluateAdd(StoreEvaluateSaveReq req) {
        Long userId = SecurityUtils.getUserId();
        IPointsDetailStrategy pointsDetailStrategy = pointsDetailStrategyMap.get(PointRuleConstant.STORE_EVALUATE + IPointsDetailStrategy.SUFFIX);
        // 1-分享前校验
        pointsDetailStrategy.beforeIncrease(userId);
        // 2-生成店铺信息
        Store storeEntity = StoreMapping.INSTANCE.coverStoreToRequest(req);
        Long storeId = IdWorker.getId();
        storeEntity.setStoreId(storeId);
        storeEntity.setCreatedName(SecurityUtils.getUsername());
        storeEntity.setCreatedBy(userId);
        storeEntity.setSourceCode(StoreSourceEnum.EVALUATE.getCode());
        storeEntity.setCreatedTime(new Date());
        List<AttachmentResp> attachmentList = req.getAttachmentList();
        if (CollUtil.isNotEmpty(attachmentList)) {
            // 默认取第1张图片
            attachmentList.stream().filter(item -> Objects.nonNull(item.getUrl()))
                    .findFirst().ifPresent(item -> storeEntity.setImageUrl(item.getUrl()));
        }
        // 生成md5字段，判定是否有相同店铺
        String md5 = buildStoreMd5(storeEntity.getAddressId(), storeEntity.getStoreName(), storeEntity.getSourceCode(), storeEntity.getAddress());
        storeEntity.setMd5(md5);
        Store storeDB = storeService.getOne(new LambdaQueryWrapper<Store>()
                .eq(Store::getDeletedVersion, 0L)
                .eq(Store::getMd5, md5).last(" limit 1 "));

        // 更新缓存实体DTO
        StoreListDTO storeListDTO = StoreMapping.INSTANCE.coverDtoToEntity(storeEntity);
        if (Objects.nonNull(storeDB)) {
            // 店铺信息更新
            storeService.update(storeEntity, new LambdaUpdateWrapper<Store>()
                    .eq(Store::getStoreId, storeDB.getStoreId())
                    .eq(Store::getDeletedVersion, 0L));
            storeListDTO.setStoreId(storeDB.getStoreId());
        } else {
            storeService.save(storeEntity);
            // 4-店铺保存附件（图片）
            attachmentService.saveAttachmentList(storeId, req.getAttachmentList(), userId);
        }
        // 3-增加店铺评价记录
        Long sharedRecordId = IdWorker.getId();
        StoreEvaluate storeShared = StoreMapping.INSTANCE.coverObjectSharedToReq(req);
        storeShared.setStoreEvaluateId(sharedRecordId);
        storeShared.setStoreId(storeId);
        storeShared.setSourceCode(StoreSourceEnum.EVALUATE.getCode());
        storeShared.setCreatedBy(SecurityUtils.getUserId());
        storeShared.setCreatedTime(new Date());
        boolean result = storeSharedService.save(storeShared);

        // 4- 店铺评价扩展对象
        StoreEvaluateExt storeEvaluateExt = StoreEvaluateExt.builder().
                storeEvaluateExtId(IdWorker.getId())
                .storeId(storeId)
                .extObj(req.getExtObj())
                .createdBy(SecurityUtils.getUserId())
                .createdTime(new Date()).build();
        evaluateExtService.save(storeEvaluateExt);

        // 5-保存店铺评价附件
        attachmentService.saveAttachmentList(sharedRecordId, req.getAttachmentList(), userId);

        // 6-添加店铺评价统计数量
        myListener.addMyStatistics(new MyStatisticsEvent(MyStatisticsTypeEnum.STORE_EVALUATE_NUM.getBusinessType(), userId, sharedRecordId));

        // 7-分享积分
        pointsDetailStrategy.updatePoints(storeShared.getStoreEvaluateId(), userId);

        // 8- 更新缓存对象
        publisher.publishEvent(new StoreCacheEvent(storeListDTO));
        return result;
    }

    /**
     * 分页查询店铺评价
     *  分页缓存数据
     *
     * @param req
     * @return
     */
    @TenantFilter
    public IPage<StoreListDTO> storeListBySource(StoreEvaluateListReq req) {
        // 缓存数据
        String md5 = generateCacheKey(req);
        String cacheKey = MessageFormat.format(HISMK_STORE_EVALUATE_LIST_CACHE, md5);
        JSONObject cacheValue = redisService.getCacheObject(cacheKey);
        IPage<StoreListDTO> storeListDTOIPage = Objects.nonNull(cacheValue) ? JSON.parseObject(cacheValue.toString(), new TypeReference<IPage<StoreListDTO>>() {
        }) : null;
        if (Objects.isNull(storeListDTOIPage)) {
            // 分页查询数据
            storeListDTOIPage = storeSharedService.storeListBySource(new Page<>(req.getPageNum(), req.getPageSize()), StoreSourceEnum.EVALUATE.getCode());
            // 查询缓存数据-设置距离
            if (CollUtil.isNotEmpty(storeListDTOIPage.getRecords())) {
                List<StoreListDTO> records = storeListDTOIPage.getRecords();
                if (CharSequenceUtil.isNotBlank(req.getLongitude()) && CharSequenceUtil.isNotBlank(req.getLatitude())) {
                    for (StoreListDTO storeListDTO : records) {
                        // 经纬度计算
                        storeCacheService.calculateDistance(storeListDTO, Double.parseDouble(req.getLongitude()), Double.parseDouble(req.getLatitude()));
                    }
                }
            }
            redisService.setCacheObject(cacheKey, storeListDTOIPage, 1L, TimeUnit.MINUTES);
        }
        return storeListDTOIPage;
    }

    /**
     *  md5 = MD5(pageNum+pageSize + longitude + latitude)
     * @param req
     * @return
     */
    private String generateCacheKey(StoreEvaluateListReq req) {
        String brandNo = SecurityUtils.getBrand();
        return DigestUtil.md5Hex(brandNo + "_" + req.getPageNum() + "_" + req.getPageSize() + "_" + req.getLongitude() + "_" + req.getLatitude());
    }

    /**
     * 店铺详情
     *
     * @param req
     * @return
     */
    public StoreListDTO findStoreDetail(StoreDetailReq req) {
        Long storeId = req.getStoreId();
        Store store = storeService.getById(storeId);
        // 如果查询的店铺为空
        if (Objects.isNull(store)) {
            throw new ServiceException(" the store is not exist. ");
        }
        if (BlockTypeEnum.YES.getDbValue().equals(store.getHasBlocked())) {
            throw new ServiceException(" the store had been blocked. ");
        }
        StoreListDTO storeDTO = StoreMapping.INSTANCE.coverDtoToEntity(store);
        storeDTO.setAttachmentList(attachmentService.getAttachmentDescById(store.getStoreId()));
        // 是否有我的收藏
        storeDTO.setHasOwnerCollection(userActionCacheService.getOwnerAction(SecurityUtils.getUserId(), storeDTO.getStoreId(),
                MyStatisticsTypeEnum.COLLECTION_STORE_NUM.getBusinessType()));

        // 距离计算
        if (CharSequenceUtil.isNotBlank(req.getLongitude()) && CharSequenceUtil.isNotBlank(req.getLatitude())) {
            storeCacheService.calculateDistance(storeDTO, Double.parseDouble(req.getLongitude()), Double.parseDouble(req.getLatitude()));
        }
        return storeDTO;
    }

    @TenantFilter
    public StoreEvaluateExt getEvaluateExt(Long storeId) {
        return evaluateExtService.lambdaQuery().eq(StoreEvaluateExt::getStoreId, storeId).eq(StoreEvaluateExt::getDeletedVersion, 0).one();
    }

    private String buildAddress(Address addressEntity, String address1) {
        if (CharSequenceUtil.isBlank(address1)) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        sb.append(address1);
        if (Objects.isNull(addressEntity)) {
            return sb.toString();
        }
        sb.append(CharSequenceUtil.isBlank(addressEntity.getCounty()) ? "" : addressEntity.getCounty())
                .append(" ")
                .append(CharSequenceUtil.isBlank(addressEntity.getCity()) ? "" : addressEntity.getCity())
                .append(" ")
                .append(CharSequenceUtil.isBlank(addressEntity.getArea()) ? "" : addressEntity.getArea())
        ;
        return sb.toString();
    }
}
